home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / softwareproperties / gtk / SoftwarePropertiesGtk.py < prev   
Encoding:
Python Source  |  2009-03-27  |  33.0 KB  |  838 lines

  1. #  GTK+ based frontend to software-properties
  2. #
  3. #  Copyright (c) 2004-2007 Canonical Ltd.
  4. #                2004-2005 Michiel Sikkes
  5. #
  6. #  Author: Michiel Sikkes <michiel@eyesopened.nl>
  7. #          Michael Vogt <mvo@debian.org>
  8. #          Sebastian Heinlein <glatzor@ubuntu.com>
  9. #
  10. #  This program is free software; you can redistribute it and/or
  11. #  modify it under the terms of the GNU General Public License as
  12. #  published by the Free Software Foundation; either version 2 of the
  13. #  License, or (at your option) any later version.
  14. #
  15. #  This program is distributed in the hope that it will be useful,
  16. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. #  GNU General Public License for more details.
  19. #
  20. #  You should have received a copy of the GNU General Public License
  21. #  along with this program; if not, write to the Free Software
  22. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  23. #  USA
  24.  
  25. import apt
  26. import apt_pkg
  27. import tempfile
  28. from gettext import gettext as _
  29. import os
  30. import re
  31. from xml.sax.saxutils import escape
  32.  
  33. import gtk
  34. import gtk.glade
  35. import gobject
  36.  
  37. from SimpleGladeApp import SimpleGladeApp
  38. from aptsources.sourceslist import SourceEntry
  39. from DialogAdd import DialogAdd
  40. from DialogMirror import DialogMirror
  41. from DialogEdit import DialogEdit
  42. from DialogCacheOutdated import DialogCacheOutdated
  43. from DialogAddSourcesList import DialogAddSourcesList
  44. from CdromProgress import CdromProgress
  45.  
  46. import softwareproperties
  47. import softwareproperties.distro
  48. from softwareproperties.SoftwareProperties import SoftwareProperties
  49. import softwareproperties.SoftwareProperties
  50.  
  51. (LIST_MARKUP, LIST_ENABLED, LIST_ENTRY_OBJ) = range(3)
  52.  
  53. (
  54.     COLUMN_ACTIVE,
  55.     COLUMN_DESC
  56. ) = range(2)
  57.  
  58. RESPONSE_REPLACE = 1
  59. RESPONSE_ADD = 2
  60.  
  61. # columns of the source_store
  62. (
  63.     STORE_ACTIVE, 
  64.     STORE_DESCRIPTION, 
  65.     STORE_SOURCE, 
  66.     STORE_SEPARATOR,
  67.     STORE_VISIBLE
  68. ) = range(5)
  69.  
  70. class SoftwarePropertiesGtk(SoftwareProperties,SimpleGladeApp):
  71.   def __init__(self, datadir=None, options=None, file=None, parent=None):
  72.     """ Provide a GTK based graphical user interface to configure
  73.         the used software repositories, corresponding authentication keys
  74.         and update automation """
  75.     SoftwareProperties.__init__(self, options=options, datadir=datadir)
  76.     gtk.window_set_default_icon_name("software-properties")
  77.  
  78.     SimpleGladeApp.__init__(self, datadir+"glade/main.glade",
  79.                             None, domain="software-properties")
  80.  
  81.     if parent:
  82.       self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
  83.       self.window_main.show()
  84.       self.window_main.set_transient_for(parent)
  85.  
  86.     # If externally called, reparent to external application.
  87.     self.options = options
  88.     if options and options.toplevel != None:
  89.       self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
  90.       self.window_main.show()
  91.       toplevel = gtk.gdk.window_foreign_new(int(options.toplevel))
  92.       if (toplevel):
  93.           self.window_main.window.set_transient_for(toplevel)
  94.     if options and options.open_tab:
  95.       self.notebook_main.set_current_page(int(options.open_tab))
  96.  
  97.     # Show what we have early
  98.     self.window_main.show()
  99.  
  100.     # used to store the handlers of callbacks
  101.     self.handlers = []
  102.  
  103.     # Put some life into the user interface:
  104.     self.init_popcon()
  105.     self.init_auto_update()
  106.     self.init_release_upgrades()
  107.     self.show_auto_update_level()
  108.     # Setup the key list
  109.     self.init_keys()
  110.     self.show_keys()
  111.     # Setup the ISV sources list
  112.     self.init_isv_sources()
  113.     self.show_isv_sources()
  114.     self.show_cdrom_sources()
  115.     # Setup and show the distro elements
  116.     self.init_distro()
  117.     self.show_distro()
  118.  
  119.     # Show the import/replace sources.list dialog if a file different
  120.     # to the default sources.list was specified 
  121.     # NOTE: If the file path points to the default sources.list the user
  122.     #       perhaps assumed that s-p would act like a normal editor.
  123.     #       We have got some bug reports from users calling
  124.     #       "sudo software-properties-gtk /etc/apt/sources.list" from the
  125.     #       command line.
  126.     if file != None and \
  127.        os.path.abspath(file) !=  "%s%s" % (apt_pkg.Config.FindDir("Dir::Etc"),
  128.                                            apt_pkg.Config.Find("Dir::Etc::sourcelist")):
  129.         self.open_file(file)
  130.  
  131.   def init_popcon(self):
  132.     """ If popcon is enabled show the statistics tab and an explanation
  133.         corresponding to the used distro """
  134.     is_helpful = self.get_popcon_participation()
  135.     if is_helpful != None:
  136.       self.label_popcon_desc.set_label(softwareproperties.distro.get_popcon_description(self.distro))
  137.       self.vbox_popcon.show()
  138.       self.checkbutton_popcon.set_active(is_helpful)
  139.  
  140.   def init_release_upgrades(self):
  141.     " setup the widgets that allow configuring the release upgrades "
  142.     i = self.get_release_upgrades_policy()
  143.     self.combobox_release_upgrades.set_active(i)
  144.     self.combobox_release_upgrades.connect('changed', self.on_combobox_release_upgrades_changed)
  145.  
  146.   def init_auto_update(self):
  147.     """ Set up the widgets that allow to configure the update automation """
  148.     # this maps the key (combo-box-index) to the auto-update-interval value
  149.     # where (-1) means, no key
  150.     self.combobox_interval_mapping = { 0 : 1,
  151.                                        1 : 2,
  152.                                        2 : 7,
  153.                                        3 : 14 }
  154.     self.combobox_update_interval.set_active(0)
  155.  
  156.     #update_days = apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"])
  157.  
  158.     self.combobox_update_interval.append_text(_("Daily"))
  159.     self.combobox_update_interval.append_text(_("Every two days"))
  160.     self.combobox_update_interval.append_text(_("Weekly"))
  161.     self.combobox_update_interval.append_text(_("Every two weeks"))
  162.  
  163.     model_check_interval = gtk.ListStore(gobject.TYPE_STRING,
  164.                                          gobject.TYPE_INT)
  165.     update_days = self.get_update_interval()
  166.  
  167.     # If a custom period is defined add a corresponding entry
  168.     if not update_days in self.combobox_interval_mapping.values():
  169.         if update_days > 0:
  170.             self.combobox_update_interval.append_text(_("Every %s days") 
  171.                                                       % update_days)
  172.             self.combobox_interval_mapping[4] = update_days
  173.     
  174.     for key in self.combobox_interval_mapping:
  175.       if self.combobox_interval_mapping[key] == update_days:
  176.         self.combobox_update_interval.set_active(key)
  177.         break
  178.  
  179.     if update_days >= 1:
  180.       self.checkbutton_auto_update.set_active(True)
  181.       self.combobox_update_interval.set_sensitive(True)
  182.       self.vbox_auto_updates.set_sensitive(True)
  183.     else:
  184.       self.checkbutton_auto_update.set_active(False)
  185.       self.combobox_update_interval.set_sensitive(False)
  186.       self.vbox_auto_updates.set_sensitive(False)
  187.  
  188.     self.handlers.append(
  189.         (self.checkbutton_auto_update,
  190.          self.checkbutton_auto_update.connect("toggled", 
  191.                                      self.on_auto_update_toggled)))
  192.     self.handlers.append(
  193.         (self.combobox_update_interval,
  194.          self.combobox_update_interval.connect("changed", 
  195.                                      self.on_combobox_update_interval_changed)))
  196.     self.handlers.append(
  197.         (self.radiobutton_updates_download,
  198.          self.radiobutton_updates_download.connect("toggled", 
  199.                                      self.set_update_automation_level,
  200.                                      softwareproperties.UPDATE_DOWNLOAD)))
  201.     self.handlers.append(
  202.         (self.radiobutton_updates_inst_sec,
  203.          self.radiobutton_updates_inst_sec.connect("toggled", 
  204.                                      self.set_update_automation_level,
  205.                                      softwareproperties.UPDATE_INST_SEC)))
  206.     self.handlers.append(
  207.         (self.radiobutton_updates_notify,
  208.          self.radiobutton_updates_notify.connect("toggled", 
  209.                                      self.set_update_automation_level,
  210.                                      softwareproperties.UPDATE_NOTIFY)))
  211.  
  212.   def show_auto_update_level(self):
  213.     """Represent the level of update automation in the user interface"""
  214.     level = self.get_update_automation_level()
  215.     self.block_handlers()
  216.     if level == None:
  217.         self.radiobutton_updates_inst_sec.set_inconsistent(True)
  218.         self.radiobutton_updates_download.set_inconsistent(True)
  219.         self.radiobutton_updates_notify.set_inconsistent(True)
  220.     else:
  221.         self.radiobutton_updates_inst_sec.set_inconsistent(False)
  222.         self.radiobutton_updates_download.set_inconsistent(False)
  223.         self.radiobutton_updates_notify.set_inconsistent(False)
  224.     if level == softwareproperties.UPDATE_MANUAL or \
  225.        level == softwareproperties.UPDATE_NOTIFY:
  226.         self.radiobutton_updates_notify.set_active(True)
  227.     elif level == softwareproperties.UPDATE_DOWNLOAD:
  228.         self.radiobutton_updates_download.set_active(True)
  229.     elif level == softwareproperties.UPDATE_INST_SEC:
  230.         self.radiobutton_updates_inst_sec.set_active(True)
  231.     # Unblock the toggle handlers
  232.     self.unblock_handlers()
  233.  
  234.   def init_distro(self):
  235.     """Setup the user interface elements to represent the distro"""
  236.  
  237.     # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
  238.     self.label_updates.set_label("<b>%s</b>" % (_("%s updates") %\
  239.                                                 self.distro.id))
  240.     # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
  241.     self.label_dist_name.set_label(_("%s Software") % self.distro.id)
  242.  
  243.  
  244.     self.handlers.append((self.checkbutton_source_code,
  245.                           self.checkbutton_source_code.connect("toggled",
  246.                               self.on_checkbutton_source_code_toggled)))
  247.  
  248.     # Setup the checkbuttons for the components
  249.     for checkbutton in self.vbox_dist_comps.get_children():
  250.          self.vbox_dist_comps.remove(checkbutton)
  251.     for comp in self.distro.source_template.components:
  252.         # TRANSLATORS: Label for the components in the Internet section
  253.         #              first %s is the description of the component
  254.         #              second %s is the code name of the comp, eg main, universe
  255.         label = _("%s (%s)") % (comp.get_description(), comp.name)
  256.         checkbox = gtk.CheckButton(label)
  257.  
  258.         checkbox.comp = comp
  259.         # setup the callback and show the checkbutton
  260.         self.handlers.append((checkbox,
  261.                               checkbox.connect("toggled", 
  262.                                                self.on_component_toggled, 
  263.                                                comp.name)))
  264.         self.vbox_dist_comps.add(checkbox)
  265.         checkbox.show()
  266.  
  267.     # Setup the checkbuttons for the child repos / updates
  268.     for checkbutton in self.vbox_updates.get_children():
  269.          self.vbox_updates.remove(checkbutton)
  270.     if len(self.distro.source_template.children) < 1:
  271.         self.frame_children.hide()
  272.     for template in self.distro.source_template.children:
  273.         checkbox = gtk.CheckButton(label="%s (%s)" % (template.description,
  274.                                                       template.name))
  275.         checkbox.template = template
  276.         self.handlers.append((checkbox,
  277.                               checkbox.connect("toggled",
  278.                                                self.on_checkbutton_child_toggled,
  279.                                                template)))
  280.         self.vbox_updates.add(checkbox)
  281.         checkbox.show()
  282.  
  283.  
  284.     # setup the server chooser
  285.     cell = gtk.CellRendererText()
  286.     self.combobox_server.pack_start(cell, True)
  287.     self.combobox_server.add_attribute(cell, 'text', 0)
  288.     self.handlers.append((self.combobox_server,
  289.                           self.combobox_server.connect("changed",
  290.                               self.on_combobox_server_changed)))
  291.     server_store = gtk.ListStore(gobject.TYPE_STRING,
  292.                                  gobject.TYPE_STRING,
  293.                                  gobject.TYPE_BOOLEAN)
  294.     self.combobox_server.set_model(server_store)
  295.     self.combobox_server.set_row_separator_func(self.is_row_separator, 2)
  296.  
  297.   def block_handlers(self):
  298.     for (widget, handler) in self.handlers:
  299.         widget.handler_block(handler)
  300.  
  301.   def unblock_handlers(self):
  302.     for (widget, handler) in self.handlers:
  303.         widget.handler_unblock(handler)
  304.  
  305.   def show_distro(self):
  306.     """Fill the distro user interface with life"""
  307.     self.block_handlers()
  308.     # Enable or disable the child source checkbuttons
  309.     for checkbox in self.vbox_updates.get_children():
  310.         (active, inconsistent) = self.get_comp_child_state(checkbox.template)
  311.         checkbox.set_active(active)
  312.         checkbox.set_inconsistent(inconsistent)
  313.  
  314.     # Enable or disable the component checkbuttons
  315.     for checkbox in self.vbox_dist_comps.get_children():
  316.         # check if the comp is enabled
  317.         (active, inconsistent) = self.get_comp_download_state(checkbox.comp)
  318.         checkbox.set_inconsistent(inconsistent)
  319.         checkbox.set_active(active)
  320.  
  321.     # If no components are enabled there will be no need for updates
  322.     # and source code
  323.     if len(self.distro.enabled_comps) < 1:
  324.         self.vbox_updates.set_sensitive(False)
  325.         self.checkbutton_source_code.set_sensitive(False)
  326.     else:
  327.         self.vbox_updates.set_sensitive(True)
  328.         self.checkbutton_source_code.set_sensitive(True)
  329.  
  330.     # Check for source code sources
  331.     source_code_state = self.get_source_code_state()
  332.     if source_code_state == None:
  333.         self.checkbutton_source_code.set_inconsistent(True)
  334.     elif source_code_state == True:        
  335.         self.checkbutton_source_code.set_active(True)
  336.         self.checkbutton_source_code.set_inconsistent(False)
  337.     else:
  338.         self.checkbutton_source_code.set_active(False)
  339.         self.checkbutton_source_code.set_inconsistent(False)
  340.  
  341.     # Will show a short explanation if no CDROMs are used
  342.     if len(self.get_cdrom_sources()) == 0:
  343.         self.scrolledwindow_cd.hide()
  344.         self.scrolledwindow_no_cd.show()
  345.     else:
  346.         self.scrolledwindow_cd.show()
  347.         self.scrolledwindow_no_cd.hide()
  348.  
  349.     # provide a list of mirrors
  350.     server_store = self.combobox_server.get_model()
  351.     server_store.clear()
  352.     seen_server_new = []
  353.     for (name, uri, active) in self.distro.get_server_list():
  354.         server_store.append([name, uri, False])
  355.         if [name, uri] in self.seen_server:
  356.             self.seen_server.remove([name, uri])
  357.         elif uri != None:
  358.             seen_server_new.append([name, uri])
  359.         if active == True:
  360.             self.active_server = len(server_store) - 1
  361.             self.combobox_server.set_active(self.active_server)
  362.     for [name, uri] in self.seen_server:
  363.         server_store.append([name, uri, False])
  364.     self.seen_server = seen_server_new
  365.     # add a separator and the option to choose another mirror from the list
  366.     server_store.append(["sep", None, True])
  367.     server_store.append([_("Other..."), None, False])
  368.  
  369.     # make the interface respond to user interput again
  370.     self.unblock_handlers()
  371.  
  372.     # Output a lot of debug stuff
  373.     if self.options.debug == True or self.options.massive_debug == True:
  374.         print "ENABLED COMPS: %s" % self.distro.enabled_comps
  375.         print "INTERNET COMPS: %s" % self.distro.download_comps
  376.         print "MAIN SOURCES"
  377.         for source in self.distro.main_sources:
  378.             self.print_source_entry(source)
  379.         print "CHILD SOURCES"
  380.         for source in self.distro.child_sources:
  381.             self.print_source_entry(source)
  382.         print "CDROM SOURCES"
  383.         for source in self.distro.cdrom_sources:
  384.             self.print_source_entry(source)
  385.         print "SOURCE CODE SOURCES"
  386.         for source in self.distro.source_code_sources:
  387.             self.print_source_entry(source)
  388.         print "DISABLED SOURCES"
  389.         for source in self.distro.disabled_sources:
  390.             self.print_source_entry(source)
  391.         print "ISV"
  392.         for source in self.sourceslist_visible:
  393.             self.print_source_entry(source)
  394.  
  395.   def set_update_automation_level(self, widget, state):
  396.     '''Call the backend to set the update automation level to the given 
  397.        value'''
  398.     if widget.get_active() == True:
  399.         self.vbox_auto_updates.foreach(lambda b: b.set_inconsistent(False))
  400.         SoftwareProperties.set_update_automation_level(self, state)
  401.     self.set_modified_config()
  402.  
  403.   def is_row_separator(self, model, iter, column=0):
  404.     ''' Check if a given row is a separator '''
  405.     return model.get_value(iter, column)
  406.  
  407.   def on_combobox_release_upgrades_changed(self, combobox):
  408.     """ set the release upgrades policy """
  409.     #print "on_combobox_release_upgrades_changed()"
  410.     i = combobox.get_active()
  411.     self.set_release_upgrades_policy(i)
  412.  
  413.   def on_combobox_server_changed(self, combobox):
  414.     """
  415.     Replace the servers used by the main and update sources with
  416.     the selected one
  417.     """
  418.     if combobox.get_active() == self.active_server:
  419.         return
  420.     server_store = combobox.get_model()
  421.     iter = combobox.get_active_iter()
  422.     uri = server_store.get_value(iter, 1)
  423.     name = server_store.get_value(iter, 0)
  424.     if name == _("Other..."):
  425.         dialog = DialogMirror(self.window_main, 
  426.                               self.datadir,
  427.                               self.distro,
  428.                               self.custom_mirrors)
  429.         res = dialog.run()
  430.         if res != None:
  431.             self.distro.change_server(res)
  432.             self.set_modified_sourceslist()
  433.         else:
  434.             combobox.set_active(self.active_server)
  435.     elif uri != None and len(self.distro.used_servers) > 0:
  436.         self.active_server = combobox.get_active()
  437.         self.distro.change_server(uri)
  438.         self.distro.default_server = uri
  439.         self.set_modified_sourceslist()
  440.     else:
  441.         self.distro.default_server = uri
  442.  
  443.   def on_component_toggled(self, checkbutton, comp):
  444.     """
  445.     Sync the components of all main sources (excluding cdroms),
  446.     child sources and source code sources
  447.     """
  448.     if checkbutton.get_active() == True:
  449.         self.enable_component(comp)
  450.     else:
  451.         self.disable_component(comp)
  452.     self.set_modified_sourceslist()
  453.  
  454.   def on_checkbutton_child_toggled(self, checkbutton, template):
  455.     """
  456.     Enable or disable a child repo of the distribution main repository
  457.     """
  458.     if checkbutton.get_active() == False:
  459.         self.disable_child_source(template)
  460.     else:
  461.         self.enable_child_source(template)
  462.           
  463.   def on_checkbutton_source_code_toggled(self, checkbutton):
  464.     """ Disable or enable the source code for all sources """
  465.     if checkbutton.get_active() == True:
  466.         self.enable_source_code_sources()
  467.     else:
  468.         self.disable_source_code_sources()
  469.  
  470.   def on_checkbutton_popcon_toggled(self, widget):
  471.     """ The user clicked on the popcon paritipcation button """
  472.     self.set_popcon_pariticipation(widget.get_active())
  473.  
  474.   def open_file(self, file):
  475.     """Show a confirmation for adding the channels of the specified file"""
  476.     dialog = DialogAddSourcesList(self.window_main,
  477.                                   self.sourceslist,
  478.                                   self.render_source,
  479.                                   self.get_comparable,
  480.                                   self.datadir,
  481.                                   file)
  482.     (res, new_sources) = dialog.run()
  483.     if res == RESPONSE_REPLACE:
  484.         self.sourceslist.list = []
  485.     if res in (RESPONSE_ADD, RESPONSE_REPLACE):
  486.         for source in new_sources:
  487.             self.sourceslist.add(source.type,
  488.                                  source.uri,
  489.                                  source.dist,
  490.                                  source.comps,
  491.                                  source.comment)
  492.         self.set_modified_sourceslist()
  493.  
  494.   def on_sources_drag_data_received(self, widget, context, x, y,
  495.                                      selection, target_type, timestamp):
  496.       """Extract the dropped file pathes and open the first file, only"""
  497.       uri = selection.data.strip()
  498.       uri_splitted = uri.split()
  499.       if len(uri_splitted)>0:
  500.           self.open_file(uri_splitted[0])
  501.  
  502.   def hide(self):
  503.     self.window_main.hide()
  504.     
  505.   def init_isv_sources(self):
  506.     """
  507.     Read all valid sources into our ListStore
  508.     """
  509.     # STORE_ACTIVE - is the source enabled or disabled
  510.     # STORE_DESCRIPTION - description of the source entry
  511.     # STORE_SOURCE - the source entry object
  512.     # STORE_SEPARATOR - if the entry is a separator
  513.     # STORE_VISIBLE - if the entry is shown or hidden
  514.     self.cdrom_store = gtk.ListStore(gobject.TYPE_BOOLEAN, 
  515.                                      gobject.TYPE_STRING,
  516.                                      gobject.TYPE_PYOBJECT,
  517.                                      gobject.TYPE_BOOLEAN,
  518.                                      gobject.TYPE_BOOLEAN)
  519.     self.treeview_cdroms.set_model(self.cdrom_store)
  520.     self.source_store = gtk.ListStore(gobject.TYPE_BOOLEAN, 
  521.                                       gobject.TYPE_STRING,
  522.                                       gobject.TYPE_PYOBJECT,
  523.                                       gobject.TYPE_BOOLEAN,
  524.                                       gobject.TYPE_BOOLEAN)
  525.     self.treeview_sources.set_model(self.source_store)
  526.     self.treeview_sources.set_row_separator_func(self.is_separator,
  527.                                                  STORE_SEPARATOR)
  528.  
  529.     cell_desc = gtk.CellRendererText()
  530.     cell_desc.set_property("xpad", 2)
  531.     cell_desc.set_property("ypad", 2)
  532.     col_desc = gtk.TreeViewColumn(_("Software Sources"), cell_desc,
  533.                                   markup=COLUMN_DESC)
  534.     col_desc.set_max_width(1000)
  535.  
  536.     cell_toggle = gtk.CellRendererToggle()
  537.     cell_toggle.set_property("xpad", 2)
  538.     cell_toggle.set_property("ypad", 2)
  539.     self.handlers.append([cell_toggle,
  540.                           cell_toggle.connect('toggled', 
  541.                                               self.on_isv_source_toggled, 
  542.                                               self.cdrom_store)])
  543.     col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
  544.                                     active=COLUMN_ACTIVE)
  545.  
  546.     self.treeview_cdroms.append_column(col_active)
  547.     self.treeview_cdroms.append_column(col_desc)
  548.  
  549.     cell_desc = gtk.CellRendererText()
  550.     cell_desc.set_property("xpad", 2)
  551.     cell_desc.set_property("ypad", 2)
  552.     col_desc = gtk.TreeViewColumn(_("Software Sources"), cell_desc,
  553.                                   markup=COLUMN_DESC)
  554.     col_desc.set_max_width(1000)
  555.  
  556.     cell_toggle = gtk.CellRendererToggle()
  557.     cell_toggle.set_property("xpad", 2)
  558.     cell_toggle.set_property("ypad", 2)
  559.     self.handlers.append([cell_toggle,
  560.                           cell_toggle.connect('toggled', 
  561.                                               self.on_isv_source_toggled, 
  562.                                               self.source_store)])
  563.     col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
  564.                                     active=COLUMN_ACTIVE)
  565.  
  566.     self.treeview_sources.append_column(col_active)
  567.     self.treeview_sources.append_column(col_desc)
  568.     # drag and drop support for sources.list
  569.     self.treeview_sources.drag_dest_set(gtk.DEST_DEFAULT_ALL, \
  570.                                         [('text/uri-list',0, 0)], \
  571.                                         gtk.gdk.ACTION_COPY)
  572.     self.treeview_sources.connect("drag_data_received",\
  573.                                   self.on_sources_drag_data_received)
  574.  
  575.   def on_isv_source_activate(self, treeview, path, column):
  576.     """Open the edit dialog if a channel was double clicked"""
  577.     self.on_edit_clicked(treeview)
  578.  
  579.   def on_treeview_sources_cursor_changed(self, treeview):
  580.     """Enable the buttons remove and edit if a channel is selected"""
  581.     sel = self.treeview_sources.get_selection()
  582.     (model, iter) = sel.get_selected()
  583.     if iter:
  584.         self.button_edit.set_sensitive(True)
  585.         self.button_remove.set_sensitive(True)
  586.     else:
  587.         self.button_edit.set_sensitive(False)
  588.         self.button_remove.set_sensitive(False)
  589.   
  590.   def on_isv_source_toggled(self, cell_toggle, path, store):
  591.     """Enable or disable the selected channel"""
  592.     #FIXME cdroms need to disable the comps in the childs and sources
  593.     iter = store.get_iter((int(path),))
  594.     source_entry = store.get_value(iter, STORE_SOURCE) 
  595.     self.toggle_source_use(source_entry)
  596.  
  597.   def init_keys(self):
  598.     """Setup the user interface parts needed for the key handling"""
  599.     self.keys_store = gtk.ListStore(str)
  600.     self.treeview2.set_model(self.keys_store)
  601.     tr = gtk.CellRendererText()
  602.     keys_col = gtk.TreeViewColumn("Key", tr, text=0)
  603.     self.treeview2.append_column(keys_col)
  604.  
  605.   #FIXME revert automation settings too
  606.   def on_button_revert_clicked(self, button):
  607.     """Restore the source list from the startup of the dialog"""
  608.     SoftwareProperties.revert(self)
  609.     self.set_modified_sourceslist()
  610.     self.show_auto_update_level()
  611.     self.button_revert.set_sensitive(False)
  612.     self.modified_sourceslist = False
  613.  
  614.   def set_modified_config(self):
  615.     """The config was changed and now needs to be saved and reloaded"""
  616.     SoftwareProperties.set_modified_config(self)
  617.     self.button_revert.set_sensitive(True)
  618.  
  619.   def set_modified_sourceslist(self):
  620.     """The sources list was changed and now needs to be saved and reloaded"""
  621.     SoftwareProperties.set_modified_sourceslist(self)
  622.     self.show_distro()
  623.     self.show_isv_sources()
  624.     self.show_cdrom_sources()
  625.     self.button_revert.set_sensitive(True)
  626.  
  627.   def show_isv_sources(self):
  628.     """ Show the repositories of independent software vendors in the
  629.         third-party software tree view """
  630.     self.source_store.clear()
  631.  
  632.     for source in self.get_isv_sources():
  633.         contents = self.render_source(source)
  634.         self.source_store.append([not source.disabled, contents,
  635.                                   source, False, True])
  636.  
  637.     (path_x, path_y) = self.treeview_sources.get_cursor()
  638.     if len(self.source_store) < 1 or path_x <0:
  639.         self.button_remove.set_sensitive(False)
  640.         self.button_edit.set_sensitive(False)
  641.  
  642.   def show_cdrom_sources(self):
  643.     """ Show CD-ROM/DVD based repositories of the currently used distro in
  644.         the CDROM based sources list """
  645.     self.cdrom_store.clear()
  646.     for source in self.get_cdrom_sources():
  647.         contents = self.render_source(source)
  648.         self.cdrom_store.append([not source.disabled, contents,
  649.                                 source, False, True])
  650.     
  651.   def is_separator(self, model, iter, column):
  652.     """ Return true if the selected row is a separator """
  653.     try:
  654.       return model.get_value(iter, column)
  655.     except Exception, e:
  656.       print "is_seperator returned '%s' " % e
  657.       return False
  658.       
  659.   def show_keys(self):
  660.     self.keys_store.clear()
  661.     for key in self.apt_key.list():
  662.       self.keys_store.append([key])
  663.  
  664.   def on_combobox_update_interval_changed(self, widget):
  665.     """Set the update automation interval to the chosen one"""
  666.     i = self.combobox_update_interval.get_active()
  667.     if i != -1:
  668.         value = self.combobox_interval_mapping[i]
  669.         self.set_update_interval(value)
  670.  
  671.   def on_auto_update_toggled(self, widget):
  672.     """Enable or disable automatic updates and modify the user interface
  673.        accordingly"""
  674.     if self.checkbutton_auto_update.get_active():
  675.       self.combobox_update_interval.set_sensitive(True)
  676.       self.vbox_auto_updates.set_sensitive(True)
  677.       # if no frequency was specified use daily
  678.       i = self.combobox_update_interval.get_active()
  679.       if i == -1:
  680.           i = 0
  681.           self.combobox_update_interval.set_active(i)
  682.       value = self.combobox_interval_mapping[i]
  683.       # A little hack to re-set the former selected update automation level
  684.       self.vbox_auto_updates.foreach(lambda b: b.toggled())
  685.     else:
  686.       self.combobox_update_interval.set_sensitive(False)
  687.       self.vbox_auto_updates.set_sensitive(False)
  688.       SoftwareProperties.set_update_automation_level(self, None)
  689.       value = 0
  690.     self.set_update_interval(str(value))
  691.  
  692.   def on_add_clicked(self, widget):
  693.     """Show a dialog that allows to enter the apt line of a to be used repo"""
  694.     dialog = DialogAdd(self.window_main, self.sourceslist,
  695.                        self.datadir, self.distro)
  696.     line = dialog.run()
  697.     if line != None:
  698.       self.add_source_from_line(line)
  699.       self.set_modified_sourceslist()
  700.       
  701.   def on_edit_clicked(self, widget):
  702.     """Show a dialog to edit an ISV source"""
  703.     sel = self.treeview_sources.get_selection()
  704.     (model, iter) = sel.get_selected()
  705.     if not iter:
  706.       return
  707.     source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
  708.     dialog = DialogEdit(self.window_main, self.sourceslist,
  709.                         source_entry, self.datadir)
  710.     if dialog.run() == gtk.RESPONSE_OK:
  711.         self.set_modified_sourceslist()
  712.  
  713.   # FIXME:outstanding from merge
  714.   def on_isv_source_activated(self, treeview, path, column):
  715.      """Open the edit dialog if a channel was double clicked"""
  716.      # check if the channel can be edited
  717.      if self.button_edit.get_property("sensitive") == True:
  718.          self.on_edit_clicked(treeview)
  719.  
  720.   # FIXME:outstanding from merge
  721.   def on_treeview_sources_cursor_changed(self, treeview):
  722.     """set the sensitiveness of the edit and remove button
  723.        corresponding to the selected channel"""
  724.     sel = self.treeview_sources.get_selection()
  725.     (model, iter) = sel.get_selected()
  726.     if not iter:
  727.         # No channel is selected, so disable edit and remove
  728.         self.button_edit.set_sensitive(False)
  729.         self.button_remove.set_sensitive(False)
  730.         return
  731.     # allow to remove the selected channel
  732.     self.button_remove.set_sensitive(True)
  733.     # disable editing of cdrom sources
  734.     source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
  735.     if source_entry.uri.startswith("cdrom:"):
  736.         self.button_edit.set_sensitive(False)
  737.     else:
  738.         self.button_edit.set_sensitive(True)
  739.  
  740.   def on_remove_clicked(self, widget):
  741.     """Remove the selected source"""
  742.     model = self.treeview_sources.get_model()
  743.     (path, column) = self.treeview_sources.get_cursor()
  744.     iter = model.get_iter(path)
  745.     if iter:
  746.       self.remove_source(model.get_value(iter, LIST_ENTRY_OBJ))
  747.       self.set_modified_sourceslist()
  748.  
  749.   def add_key_clicked(self, widget):
  750.     """Provide a file chooser that allows to add the gnupg of a trusted
  751.        software vendor"""
  752.     chooser = gtk.FileChooserDialog(title=_("Import key"),
  753.                                     parent=self.window_main,
  754.                                     buttons=(gtk.STOCK_CANCEL,
  755.                                              gtk.RESPONSE_REJECT,
  756.                                              gtk.STOCK_OK,gtk.RESPONSE_ACCEPT))
  757.     res = chooser.run()
  758.     chooser.hide()
  759.     if res == gtk.RESPONSE_ACCEPT:
  760.       if not self.add_key(chooser.get_filename()):
  761.         error(self.window_main,
  762.               _("Error importing selected file"),
  763.               _("The selected file may not be a GPG key file " \
  764.                 "or it might be corrupt."))
  765.       self.show_keys()
  766.  
  767.   def remove_key_clicked(self, widget):
  768.     """Remove a trusted software vendor key"""
  769.     selection = self.treeview2.get_selection()
  770.     (model,a_iter) = selection.get_selected()
  771.     if a_iter == None:
  772.         return
  773.     key = model.get_value(a_iter,0)
  774.     if not self.remove_key(key[:8]):
  775.       error(self.main,
  776.         _("Error removing the key"),
  777.         _("The key you selected could not be removed. "
  778.           "Please report this as a bug."))
  779.     self.show_keys()
  780.  
  781.   def on_restore_clicked(self, widget):
  782.     """Restore the original keys"""
  783.     self.apt_key.update()
  784.     self.show_keys()
  785.  
  786.   def on_delete_event(self, widget, args):
  787.     """Close the window if requested"""
  788.     self.on_close_button(widget)
  789.  
  790.   def on_close_button(self, widget):
  791.     """Show a dialog that a reload of the channel information is required
  792.        only if there is no parent defined"""
  793.     if (self.modified_sourceslist == True and
  794.         self.options.no_update == False):
  795.         d = DialogCacheOutdated(self.window_main,
  796.                                 self.datadir)
  797.         res = d.run()
  798.     self.quit()
  799.  
  800.   def on_button_add_cdrom_clicked(self, widget):
  801.     '''Show a dialog that allows to add a repository located on a CDROM
  802.        or DVD'''
  803.     # testing
  804.     #apt_pkg.Config.Set("APT::CDROM::Rename","true")
  805.  
  806.     saved_entry = apt_pkg.Config.Find("Dir::Etc::sourcelist")
  807.     tmp = tempfile.NamedTemporaryFile()
  808.     apt_pkg.Config.Set("Dir::Etc::sourcelist",tmp.name)
  809.     progress = CdromProgress(self.datadir,self.window_main)
  810.     cdrom = apt_pkg.GetCdrom()
  811.     # if nothing was found just return
  812.     try:
  813.       res = cdrom.Add(progress)
  814.     except SystemError, msg:
  815.       #print "aiiiieeee, exception from cdrom.Add() [%s]" % msg
  816.       progress.close()
  817.       dialog = gtk.MessageDialog(parent=self.window_main,
  818.                                  flags=gtk.DIALOG_MODAL,
  819.                                  type=gtk.MESSAGE_ERROR,
  820.                                  buttons=gtk.BUTTONS_OK,
  821.                                  message_format=None)
  822.       dialog.set_markup(_("<big><b>Error scanning the CD</b></big>\n\n%s")%msg)
  823.       res = dialog.run()
  824.       dialog.destroy()
  825.       return
  826.     apt_pkg.Config.Set("Dir::Etc::sourcelist",saved_entry)
  827.     if res == False:
  828.       progress.close()
  829.       return
  830.     # read tmp file with source name (read only last line)
  831.     line = ""
  832.     for x in open(tmp.name):
  833.       line = x
  834.     if line != "":
  835.       full_path = "%s%s" % (apt_pkg.Config.FindDir("Dir::Etc"),saved_entry)
  836.       self.sourceslist.list.append(SourceEntry(line,full_path))
  837.       self.set_modified_sourceslist()
  838.